package io.renren.modules.app.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.renren.common.config.BaseConstant;
import io.renren.common.enums.ErrorCode;
import io.renren.common.exception.RRException;
import io.renren.common.utils.AliMessageSendComponent;
import io.renren.common.utils.CheckParam;
import io.renren.common.utils.RedisUtils;
import io.renren.common.utils.SnowflakeIdWorker;
import io.renren.common.weixin.WxPayCreateSign;
import io.renren.common.weixin.XmlUtils;
import io.renren.modules.app.entity.CustomerEntity;
import io.renren.modules.app.entity.OrderEntity;
import io.renren.modules.app.req.weixin.WeChatUnifiedPayReq;
import io.renren.modules.app.service.OrderService;
import io.renren.modules.app.service.WeChatPayService;
import io.renren.modules.app.utils.AuthUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author create by MR.XU email:260139486@qq.com
 * @projectName map-clothes-app-api
 * @Description: 微信支付相关服务方法实现
 * @date 2021-01-19 10:04:49
 */
@Service("weChatPayService")
@Slf4j
public class WeChatPayServiceImpl implements WeChatPayService {

    @Resource
    private BaseConstant baseConstant;

    @Resource
    private RedisUtils redisUtils;

    @Resource
    private OrderService orderService;

    @Resource
    private AliMessageSendComponent aliMessageSendComponent;

    //锁
    private Lock lock = new ReentrantLock();


    /**
      * @description: 微信支付前置---统一下单
      * @auther: create by MR.XU email:260139486@qq.com
      * @date 2021-01-19 10:04:49
      * @param weChatUnifiedPayReq
      * @return
      */
    @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public Map<String, Object> unifiedPaymentOrder(WeChatUnifiedPayReq weChatUnifiedPayReq, HttpServletRequest request, HttpServletResponse response) throws IOException {
        lock.lock();
        try {
            CustomerEntity customer = AuthUtils.getUser();
            OrderEntity orderEntity = orderService.getOne(
                    new QueryWrapper<OrderEntity>()
                            .eq("id",weChatUnifiedPayReq.getOrderId())
                            .eq("user_id",customer.getUserId())
                            .eq("status",1)
            );
            if(CheckParam.isNull(orderEntity)){
                throw new RRException(ErrorCode.NOT_EXSIZE_ORDER.getMessage(),ErrorCode.NOT_EXSIZE_ORDER.getCode());
            }
            String redisJsonStr = null;
            String openId = "";
            //======判断是否已经发起过支付请求
            //判断是否已经发起过统一下单  userId+orderNo  判断是否是同一笔订单
            String redisKey = customer.getUserId()+weChatUnifiedPayReq.getOrderNo();
            log.info("=================[统一下单接口] 用户端获取支付缓存redisKey:{}",redisKey);
            redisJsonStr = redisUtils.get(redisKey);
            //获取openid
            openId = customer.getOpenId();
            //判断是否有缓存
            if (CheckParam.isNull(redisJsonStr)){
                //微信统一下单开始==================
                //1.生成随机数
                String nonceStr = SnowflakeIdWorker.generateNonceStr();
                //2.生成支付订单号---根据支付类型
                String payOrderNo = SnowflakeIdWorker.getNextPayOrderNo();
                //3.处理金额:分为单位,不能有小数点
                BigDecimal payAmount = orderEntity.getTotalPrice().multiply(BigDecimal.valueOf(100L)).setScale(0,BigDecimal.ROUND_DOWN);
                //4. 封装参数
                SortedMap<String, String> sortMap = new TreeMap<>();
                // 微信支付所需要的appId---判断商家端和用户端
                sortMap.put("appid",baseConstant.getAppId());

                // 订单内容
                sortMap.put("body",weChatUnifiedPayReq.getPayBody());
                // 微信商户号
                sortMap.put("mch_id",baseConstant.getMchId());
                // 随机数
                sortMap.put("nonce_str", nonceStr);
                // 微信异步通知地址
                sortMap.put("notify_url",baseConstant.getNotifyUrl());
                //设置openId
                sortMap.put("openid",openId);
                // 商户订单号
                sortMap.put("out_trade_no", payOrderNo);
                // 终端ip(下单生成机器的ip),可以获取支付接口的请求IP
                sortMap.put("spbill_create_ip", weChatUnifiedPayReq.getUserPhoneIp());
                // 订单金额
                sortMap.put("total_fee",payAmount.toString());
                // 支付类型
                sortMap.put("trade_type",baseConstant.getTradeType());

                log.info(">>>>>>>>>>>>>>>>>>>>>>>>>>微信支付请求参数>>>>>>>>>>>>>>>>>>>>>"+JSON.toJSONString(sortMap));

                //5. 获得微信支付验签
                String sign = WxPayCreateSign.createSign( sortMap, baseConstant.getPartnerKey());//----生产环境
//            String sign = WxPayCreateSign.createSign( sortMap, sandKey);//沙箱环境使用沙箱秘钥
                sortMap.put("sign", sign);
                //6. 封装成xml格式
                String xmlStr = XmlUtils.toXml(sortMap);
                log.info("================[微信支付]统一下单请求参数:{}",xmlStr);
                //转换为请求参数
                StringEntity entityParams = new StringEntity(xmlStr,"utf-8");
                //7. 发起下单请求
                Map<String, Object> payResultMap = requestWxPay(entityParams, baseConstant.getPayUrl(), baseConstant.getPartnerKey());
                String payResulCode = (String) payResultMap.get("code");
                log.info("============[预支付返回map] 数据:{}",JSON.toJSONString(payResultMap));
                if ("200".equalsIgnoreCase(payResulCode)){
                    //8. 判断是否请求成功,产生微信交易流水号
                    Map<String,String> resMap = (Map<String, String>) payResultMap.get("result");
                    String prepayId = resMap.get("prepayid");
                    log.info("=======[预支付订单号]: 数据:{}",prepayId);
                    if (!CheckParam.isNull(prepayId)){
                        payResultMap.put("payOrderNo",payOrderNo);//设置支付订单号
                        log.info("=========[创建支付订单] 支付订单号:{} ,微信预支付订单号:{}",payOrderNo,prepayId);
                        //10. 二次签名
                        log.info("==================二次签名=====================");
                        SortedMap<String, String> newUserSortMap = new TreeMap<>();
                        newUserSortMap.put("appId", baseConstant.getUserAppId());
                        newUserSortMap.put("nonceStr", resMap.get("noncestr"));
                        String newPackage = "prepay_id=" + prepayId;
                        newUserSortMap.put("package", newPackage);
                        newUserSortMap.put("signType","MD5");
                        newUserSortMap.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
                        String newUserSign = WxPayCreateSign.createSign( newUserSortMap, baseConstant.getPartnerKey());
                        log.info("====================二次签名数据:{}",newUserSign);
                        payResultMap.put("paySign",newUserSign);
                        payResultMap.put("timeStamp",newUserSortMap.get("timeStamp"));

                        //9. 把用户预支付的请求成功信息存入缓存,保存30分钟========做重复提交预支付订单处理
                        String userRedisKey = customer.getUserId()+"-"+weChatUnifiedPayReq.getOrderNo();
                        String userJsonStr = JSON.toJSONString(payResultMap);
//                        redisUtils.set(userRedisKey,userJsonStr,30L, TimeUnit.MINUTES);//存入缓存,30分钟失效
                        redisUtils.set(userRedisKey,userJsonStr);//存入缓存,30分钟失效
                        //11. 创建支付交易记录
//                        createTransRecord(payOrderNo,weChatUnifiedPayReq);
                    }
                }
                return payResultMap;
            }else {
                //有缓存,直接返回
                Map<String,Object> returnMap = JSON.parseObject(redisJsonStr, Map.class);
                return returnMap;
            }
        }finally {
            lock.unlock();
        }

    }

    /**
      * @description: 创建交易记录
      * @auther: create by Administrator - MR.XU email:260139486@qq.com
      * @date 2021-01-19 10:04:49
      * @param
      * @return
      */
//    private void createTransRecord(String payOrderNo, WeChatUnifiedPayReq weChatUnifiedPayReq) {
//        //查询订单相关信息
//        UserTransRecord record = new UserTransRecord();
//        record.setPayFlag(weChatUnifiedPayReq.getPayFlag());
//        record.setPayAmount(weChatUnifiedPayReq.getPayAmount());
//        record.setPayMethod(1);//微信支付
//        record.setTransNo(payOrderNo);//交易订单号
////        record.setPrePayId(prePayId);//流水号
//        record.setTransStatus(0);//支付中
//        record.setUserId(weChatUnifiedPayReq.getUserId());
//        //获取订单信息
//        UserOrder userOrder = userOrderRepository.findByUserOrderNo(weChatUnifiedPayReq.getOrderNo());
//        record.setUserOrderId(userOrder.getUserOrderId());
//        record.setUserOrderNo(userOrder.getOrderNo());
//        record.setUserId(weChatUnifiedPayReq.getUserId());
//        record.setOrderAmount(userOrder.getOrderAmount());
//
//        //添加
//        userTransRecordRepository.insert(record);
//
//        return;
//
//    }

    /**
      * @description: 微信支付回调
      * @auther: create by MR.XU email:260139486@qq.com
      * @date 2021-01-19 10:04:49
      * @param
      * @return
      */
    @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void WxPayNotify(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        resp.setHeader("Content-type", "text/html;charset=UTF-8");
        //1. 获取返回接口
        String resultStr = XmlUtils.parseRequst(req);
        //2. 创建返回数据
        SortedMap<String,String> returnMap = new TreeMap<>();
        log.info("===============[微信支付]微信回调通知结果:{}",resultStr);
        if (!CheckParam.isNull(resultStr)){
            Map<String, String> resultMap = XmlUtils.toMap(resultStr.getBytes(), "utf-8");
            log.info("==============[解析map] 数据:{}",resultMap.toString());
            //3. 判断是否支付成功
            if(resultMap.get("return_code").equalsIgnoreCase("SUCCESS")){
                //4. 签名校验
                boolean flag = WxPayCreateSign.checkSign(resultMap,baseConstant.getPartnerKey());
                log.info("================[微信支付]微信回调签名验证:{}",flag);
//                boolean flag =true;//=======沙箱测试不需要验签
                if (flag){
                    //==========支付成功+验签成功
                    //5. 获取微信支付订单号(微信生成的微信支付订单号)-----交易流水号
                    String wxTransId = resultMap.get("transaction_id");
                    log.info("============[微信支付订单号] 返回数据:{}",wxTransId);
                    // 5. 获取平台生成的支付订单号
                    String payOrderNo = resultMap.get("out_trade_no");
                    //6. 修改交易记录+修改订单+通知商户发货+删除支付缓存
//                    UserTransRecord record = userTransRecordRepository.findByTransNo(payOrderNo);
//                    record.setTransStatus(2);//交易成功
//                    record.setBatchNo(wxTransId);
//                    userTransRecordRepository.updateByPrimaryKey(record);
                    //修改订单状态
                    log.info("=============修改订单状态=================");
//                    if (record.getPayFlag() == 1){
//                        UserOrder userOrder = userOrderRepository.findByUserOrderId(record.getUserOrderId());
//                        userOrder.setOrderStatus(2);//支付成功
//                        userOrderRepository.updateByPrimaryKey(userOrder);
//                        //todo 发送商家接单短信
//                        //获取商家信息
//                        ShopInfo shopInfo = shopInfoRepository.selectByMerchantId(userOrder.getMerchantId());
//                        aliMessageSendComponent.sendMerchantMessage(shopInfo.getShopMobile(),shopInfo.getShopName(),userOrder.getOrderNo());
//                    }else {
//                        MerchantOrder merchantOrder = merchantOrderRepository.findByMerchantOrderNo(record.getMerchantOrderNo());
//                        merchantOrder.setOrderStatus(2);//支付成功
//                        merchantOrderRepository.updateByPrimaryKey(merchantOrder);
//                    }
                    //验签成功
                    returnMap.put("return_code","SUCCESS");
                    resultMap.put("return_msg","OK");
                }else {
                    //验签失败
                    returnMap.put("return_code","FAIL");
                    resultMap.put("return_msg","签名不正确");
                }
            }else {
                //支付失败
                returnMap.put("return_code","FAIL");
                resultMap.put("return_msg","支付失败");
            }
        }
        //通知微信已接收
        String xmlStr = XmlUtils.parseXML(returnMap);
        resp.getWriter().write(xmlStr);
    }


    /**
      * @description: 微信支付结果查询
      * @auther: create by MR.XU email:260139486@qq.com
      * @date 2021-01-19 10:04:49
      * @param weChatUnifiedPayReq
      * @return
      */
    @Override
    public Map<String, String> WxPayQueryResult(WeChatUnifiedPayReq weChatUnifiedPayReq, HttpServletRequest req, HttpServletResponse resp) throws IOException {
        //创建返回参数
        Map<String,String> returnMap = new HashMap<>();
        //生成随机数
        String nonce_str = SnowflakeIdWorker.generateNonceStr();
        //创建请求参数
        SortedMap<String,String> sortedMap = new TreeMap<>();
        sortedMap.put("appid",baseConstant.getUserAppId());
        sortedMap.put("mch_id",baseConstant.getMchId());
        sortedMap.put("nonce_str",nonce_str);
        sortedMap.put("transaction_id",weChatUnifiedPayReq.getWxTransId());
        //生成签名
        String sign = WxPayCreateSign.createSign(sortedMap, baseConstant.getPartnerKey());
        sortedMap.put("sign",sign);
        //转出xml
        String xmlStr = XmlUtils.toXml(sortedMap);
        StringEntity params = new StringEntity(xmlStr,"utf-8");
        //=====发起请求
        Map<String, String> resultMap = doPost(params, baseConstant.getQueUrl());
        if ("SUCCESS".equalsIgnoreCase(resultMap.get("return_code")) && "SUCCESS".equalsIgnoreCase(resultMap.get("result_code"))){
            //处理金额
            BigDecimal total_fee = new BigDecimal(resultMap.get("total_fee"));
            BigDecimal payAmount = total_fee.divide(new BigDecimal(100));
            //获取本地交易记录订单---支付流水号
            //UserTransRecord record = userTransRecordRepository.findByWxTransId(weChatUnifiedPayReq.getWxTransId(),weChatUnifiedPayReq.getUserId());
//            UserTransRecord record = userTransRecordRepository.findByTransNo(weChatUnifiedPayReq.getPayOrderNo());
            AtomicBoolean flag = new AtomicBoolean(true);
//            if (record.getTransStatus() != 2 || payAmount.compareTo(record.getPayAmount()) != 0 ){
//                flag.set(false);
//            }
            //返回设置
            if (flag.get()){
                returnMap.put("return_code","SUCCESS");
                resultMap.put("return_msg","OK");
            }else {
                returnMap.put("return_code","FAIL");
                resultMap.put("return_msg","获取结果失败");
            }
        }else {
            returnMap.put("return_code","FAIL");
            resultMap.put("return_msg",resultMap.get("return_msg"));

        }
        return returnMap;
    }

    /**
      * @description: 发起微信请求
      * @auther: create by MR.XU email:260139486@qq.com
      * @date 2021-01-19 10:04:49
      * @param
      * @return
      */

    private Map<String,String> doPost(StringEntity params,String url) throws IOException {
        CloseableHttpResponse response = null;
        CloseableHttpClient client = null;
        Map<String, String> resultMap = new HashMap<>();
        try {
            //创建请求url
            HttpPost post = new HttpPost(url);
            //设置请求参数
            post.setEntity(params);
            client = HttpClients.createDefault();
            //接收返回参数
            response = client.execute(post);
            //提取参数
            resultMap = XmlUtils.toMap(EntityUtils.toByteArray(response.getEntity()), "utf-8");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(response != null){
                response.close();
            }
            if(client != null){
                client.close();
            }
        }

        return resultMap;
    }

    /**
      * @description: 请求微信统一下单接口
      * @auther: create by MR.XU email:260139486@qq.com
      * @date 2021-01-19 10:04:49
      * @param
      * @return
      */

    private Map<String,Object> requestWxPay(StringEntity entityParams,String payUrl,String key) throws IOException {
        Map<String,Object> payResultMap = new HashMap<>();
        // 发起预支付
        Map<String, String> resultMap = doPost(entityParams, payUrl);
        log.info("=======微信支付下单结果：" + XmlUtils.toXml(resultMap));
        //判断
        if(StringUtils.equalsIgnoreCase("SUCCESS", resultMap.get("return_code")) && StringUtils.equalsIgnoreCase("SUCCESS", resultMap.get("result_code"))) {
            //预支付成功
            resultMap.put("timestamp", SnowflakeIdWorker.getWxPayTime());
            resultMap.put("package", "Sign=WXPay");
            //重新签名下单返回的参数，传给前端
            payResultMap.put("result", newSignReturnParam(resultMap,key) );
            payResultMap.put("code","200");
        } else {
            //预支付失败
            payResultMap.put("result", resultMap.get("return_msg"));
            payResultMap.put("code","500");
            log.info("微信支付失败：" + resultMap.get("return_msg"));
        }
        return payResultMap;
    }

    /**
      * @description: 封装返回app参数
      * @auther: create by MR.XU email:260139486@qq.com
      * @date 2021-01-19 10:04:49
      * @param
      * @return
      */
    private SortedMap<String,String> newSignReturnParam(Map<String, String> resultMap, String key) {
        resultMap.remove("sign");
        resultMap.remove("result_code");
        resultMap.remove("return_msg");
        resultMap.remove("return_code");
        resultMap.remove("device_info");
        resultMap.remove("trade_type");

        SortedMap<String,String> signMap = new TreeMap<>();

        //此处返回给前端调微信支付的键值必须去掉参数中间的下划线
        signMap.put("appid", resultMap.get("appid"));
        signMap.put("partnerid", resultMap.get("mch_id"));
        signMap.put("noncestr", SnowflakeIdWorker.generateNonceStr());
        signMap.put("package", resultMap.get("package"));
        signMap.put("prepayid", resultMap.get("prepay_id"));
        signMap.put("timestamp", resultMap.get("timestamp"));

        SortedMap<String,String> reqMap = convertSortMap(signMap);
        //重新签名
        String sign = WxPayCreateSign.createSign(signMap, baseConstant.getPartnerKey());
        reqMap.put("sign", sign);
        return reqMap;
    }

    /**
      * @description: 类型转换
      * @auther: create by MR.XU email:260139486@qq.com
      * @date 2021-01-19 10:04:49
      * @param
      * @return
      */

    private SortedMap<String,String> convertSortMap(Map<String, String> resultMap) {
        SortedMap<String,String> reqMap = new TreeMap<String,String>();
        for(String key : resultMap.keySet()) {
            reqMap.put(key, resultMap.get(key));
        }
        return reqMap;
    }

/**
      * @description: 获取沙箱秘钥
      * @auther: create by MR.XU email:260139486@qq.com
      * @date 2021-01-19 10:04:49
      * @return
      */

    private String getWxSandSignKey() throws IOException {
        String flag = "";
        //获取沙箱秘钥
        String nor = SnowflakeIdWorker.generateNonceStr();
        SortedMap<String,String> sandMap = new TreeMap<>();
        sandMap.put("mch_id",baseConstant.getMchId());
        sandMap.put("nonce_str",nor);
        sandMap.put("sign_type","MD5");

        String sandSign = WxPayCreateSign.createSign( sandMap, baseConstant.getPartnerKey());
        sandMap.put("sign",sandSign);
        StringEntity sandEntity = new StringEntity(XmlUtils.toXml(sandMap),"utf-8");
        Map<String, String> resultMap = doPost(sandEntity, baseConstant.getSandSignKeyUrl());
        if (resultMap.get("return_code").equalsIgnoreCase("SUCCESS")){
            flag = resultMap.get("sandbox_signkey");
        }
        return flag;
    }
}
